﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;

using Microsoft.Practices.EnterpriseLibrary.PolicyInjection;
using Microsoft.Practices.Unity;
using Microsoft.Practices.Unity.Configuration;

namespace WFL.IoCUnity
{
    public static class UnityUtility
    {
        private static Dictionary<UnityConfigurationAttribute, IUnityContainer> _containers
            = new Dictionary<UnityConfigurationAttribute, IUnityContainer>();

        private static IUnityContainer GetUnityContainer(UnityConfigurationAttribute attr)
        {
            if (_containers.ContainsKey(attr))
            {
                return _containers[attr];
            }
            lock (_containers)
            {
                if (_containers.ContainsKey(attr))
                {
                    return _containers[attr];
                }
                else
                {
                    var unitySection = attr.GetUnityConfigurationSection();
                    if (unitySection != null)
                    {
                        var container = new UnityContainer().LoadConfiguration(unitySection
                            , string.IsNullOrEmpty(attr.Container) ? unitySection.Containers.Default.Name : attr.Container);
                        _containers.Add(attr, container);
                        return container;
                    }
                }
            }
            return null;
        }

        public static T Resolve<T>(params ResolverOverride[] resolverOverrides) where T : class
        {
            return Resolve(typeof(T), null, resolverOverrides) as T;
        }

        public static T Resolve<T>(string name, params ResolverOverride[] resolverOverrides) where T : class
        {
            return Resolve(typeof(T), name, resolverOverrides) as T;
        }

        public static object Resolve(this Type type, params ResolverOverride[] resolverOverrides)
        {
            return Resolve(type, null, resolverOverrides);
        }

        public static object Resolve(this Type type, string name, params ResolverOverride[] resolverOverrides)
        {
            var attrs = type.GetCustomAttributes(typeof(UnityConfigurationAttribute), true) as UnityConfigurationAttribute[];
            if (attrs.Length > 0)
            {
                var container = GetUnityContainer(attrs[0]);
                if (container != null)
                {
                    var obj = string.IsNullOrEmpty(name) ? container.Resolve(type, resolverOverrides) : container.Resolve(type, name, resolverOverrides);
                    if (obj != null)
                    {
                        var piabAttrs = obj.GetType().GetCustomAttributes(typeof(PolicyInjectionAttribute), true) as PolicyInjectionAttribute[];
                        if (piabAttrs.Length > 0)
                        {
                            obj = PolicyInjection.Wrap(type, obj);
                        }
                        return obj;
                    }
                }
            }
            return null;
        }

        public static IEnumerable<T> ResolveAll<T>(params ResolverOverride[] resolverOverrides) where T : class
        {
            return ResolveAll(typeof(T), resolverOverrides) as IEnumerable<T>;
        }

        public static IEnumerable<object> ResolveAll(this Type type, params ResolverOverride[] resolverOverrides)
        {
            var attrs = type.GetCustomAttributes(typeof(UnityConfigurationAttribute), true) as UnityConfigurationAttribute[];
            if (attrs.Length > 0)
            {
                var container = GetUnityContainer(attrs[0]);
                if (container != null)
                {
                    return container.ResolveAll(type, resolverOverrides);
                }
            }
            return null;
        }
    }
}
